home *** CD-ROM | disk | FTP | other *** search
/ BMUG Revelations / BMUG Revelations.toast / Programming / Programming Languages / UCB Logo 3.0 / sources / my_console.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-06-13  |  34.8 KB  |  2,050 lines  |  [TEXT/JV01]

  1.  
  2. /*
  3.  *  console.c
  4.  *
  5.  *  Copyright (c) 1991 Symantec Corporation.  All rights reserved.
  6.  *
  7.  */
  8.  
  9. #include <MacHeaders>
  10.  
  11. extern void redraw_graphics(), resize_record();  /* mak */
  12. extern WindowPtr graphics_window, listener_window;  /* mak */
  13.  
  14. /*  remove this line to work on ALL systems  */
  15. #include <PrintTraps.h>        /*  requires System 4.1  */
  16.  
  17. #ifndef __PRINTTRAPS__
  18. #include <Printing.h>
  19. #endif
  20.  
  21. #include "stdio.h"
  22. #include "stddef.h"
  23. #include "stdlib.h"
  24. #include "string.h"
  25. #include "signal.h"
  26. #include "errno.h"
  27. #include "console.h"
  28. #include "ansi_private.h"
  29.  
  30. struct __copt console_options = { 50, 10, "\pconsole", 8, 4, 9, 0, 25, 80, 1 };
  31. char __log_stdout;
  32.  
  33. static void InitConsole(void);
  34. static void ProcessEvent(void);
  35. static WindowPeek new_console(void);
  36. static void kill_console(void);
  37. static void closeecho(void);
  38. static WindowPeek cflush(FILE *);
  39.  
  40. static void console_exit(void);
  41. static int consoleio(FILE *, int);
  42. static pascal void myStdText(short, char *, Point, Point);
  43.  
  44. static void cleos(int);
  45. static void cleol(void);
  46. static void output(unsigned char *, short);
  47. static void overwrite(unsigned char *, long);
  48. static void blank(int, int);
  49. static void insert(char, int);
  50. static void pad(char, long, long);
  51. static void paste(int, int);
  52. static int setcursor(int);
  53. static TEPtr deactivate(void);
  54. static void strip(void);
  55. static char *copy(void);
  56. static void endcopy(void);
  57. static void newline(void);
  58. static void resize(void);
  59. static void use(WindowPeek);
  60.  
  61. static int open_console_driver(void);
  62. static int doClose(void);
  63. static int doControl(void);
  64. static void doCursor(void);
  65. static void doClick(EventRecord *);
  66. static void doZoom(Point, int);
  67. static void doGrow(Point);
  68. static void doSelect(EventRecord *);
  69. static int doCmdKey(int);
  70. static void doKey(int);
  71. static void doCut(void);
  72. static void doCopy(void);
  73. static void doPaste(void);
  74.  
  75. static void print_console(void);
  76. static void print(void);
  77.  
  78. static struct console {
  79.     WindowPeek            wp;
  80.     short                height;
  81.     short                width;
  82.     short                nrows;
  83.     short                ncols;
  84.     Point                cursor;
  85.     short                tabs;
  86.     TEHandle            hTE;
  87.     Point                limit;
  88.     Handle                pasteH;
  89.     long                pasteOfs;
  90.     long                pasteLen;
  91.     FILE                *echo2fp;
  92.     unsigned            raw : 1;
  93.     unsigned            cbreak : 1;
  94.     unsigned             edit : 1;
  95.     unsigned             reading : 1;
  96.     unsigned            inverse : 1;
  97.     unsigned            spool : 1;
  98. } c;
  99.  
  100. static char console_environment, noPrint, interrupted, paused;
  101. static short console_refnum;
  102. static MenuHandle appleMenu;
  103. static WindowPeek theConsole;
  104.  
  105. struct save {
  106.     WindowPeek            console;
  107.     GrafPtr                port;
  108. };
  109. static void setup(WindowPeek, struct save *);
  110. static void restore(struct save *);
  111.  
  112. static struct {
  113.     unsigned char    *buf;
  114.     unsigned char    *ptr;
  115.     size_t            cnt;
  116.     int                min;
  117.     int                max;
  118. } in;
  119.  
  120. struct vector {
  121.     short            vJMP;
  122.     int                (*vCode)();
  123. };
  124.  
  125. #define OFS(x)        offsetof(struct drvr, x)
  126.  
  127. static struct drvr {
  128.     short            drvrFlags, drvrDelay, drvrEMask, drvrMenu;
  129.     short            drvrOpen, drvrPrime, drvrCtl, drvrStatus, drvrClose;
  130.     char            drvrName[10];
  131.     short            drvrRTS;
  132.     struct vector    vCtl, vClose;
  133. } drvr = {
  134.     0x0760, 0, 0x016A, 0,
  135.     OFS(drvrRTS), OFS(drvrRTS), OFS(vCtl), OFS(drvrRTS), OFS(vClose),
  136.     "\p.console",
  137.     0x4E75,
  138.     { 0x4EF9 }, { 0x4EF9 }
  139. }, **drvrH;
  140.  
  141. #define hiword(x)        (((short *) &(x))[0])
  142. #define loword(x)        (((short *) &(x))[1])
  143.  
  144.  
  145. /* ---------- public entry points ---------- */
  146.  
  147.  
  148. /*
  149.  *  fopenc - open a stream on a new console
  150.  *
  151.  */
  152.  
  153. FILE *
  154. fopenc(void)
  155. {
  156.     return(freopenc(NULL, __getfile()));
  157. }
  158.  
  159.  
  160. /*
  161.  *  freopenc - reopen a stream on a new or existing console
  162.  *
  163.  *  "fp" is closed, if necessary, then opened as a console.  If "fp2"
  164.  *  is NULL, a new console is created; otherwise "fp2" must refer to
  165.  *  a console, and "fp" is made to refer to the same console.
  166.  *
  167.  */
  168.  
  169. FILE *
  170. freopenc(FILE *fp2, FILE *fp)
  171. {
  172.     if (fp == NULL)
  173.         return(NULL);
  174.     if (WWExist)
  175.         InitConsole();
  176.     fclose(fp);
  177.     fp->refnum = -1;
  178.     fp->window = fp2 ? fp2->window : new_console();
  179.     setvbuf(fp, NULL, _IOLBF, BUFSIZ);
  180.     fp->proc = consoleio;
  181.     __atexit_console(console_exit);
  182.     return(fp);
  183. }
  184.  
  185.  
  186. /*
  187.  *  cgotoxy - position cursor at <x,y>
  188.  *
  189.  *  The position of the upper left corner is <1,1>.
  190.  *
  191.  *  This routine does NOT check its arguments.  Don't place the
  192.  *  cursor off-screen!
  193.  *
  194.  */
  195.  
  196. void
  197. cgotoxy(int x, int y, FILE *fp)
  198. {
  199.     struct save save;
  200.     
  201.     setup(cflush(fp), &save);
  202.     c.cursor.h = x - 1;
  203.     c.cursor.v = y - 1;
  204.     restore(&save);
  205. }
  206.  
  207.  
  208. /*
  209.  *  cgetxy - report the current cursor position
  210.  *
  211.  *  The position of the upper left corner is <1,1>.
  212.  *
  213.  */
  214.  
  215. void
  216. cgetxy(int *x, int *y, FILE *fp)
  217. {
  218.     struct save save;
  219.     
  220.     setup(cflush(fp), &save);
  221.     *x = c.cursor.h + 1;
  222.     *y = c.cursor.v + 1;
  223.     restore(&save);
  224. }
  225.  
  226.  
  227. /*
  228.  *  ccleos - clear from cursor to end of screen
  229.  *
  230.  *  The line containing the cursor, and all following lines, are erased.
  231.  *  The cursor is left at the start of the first line erased.
  232.  *
  233.  */
  234.  
  235. void
  236. ccleos(FILE *fp)
  237. {
  238.     struct save save;
  239.     
  240.     setup(cflush(fp), &save);
  241.     cleos(c.cursor.v);
  242.     restore(&save);
  243. }
  244.  
  245.  
  246. /*
  247.  *  ccleol - clear from cursor to end of line
  248.  *
  249.  */
  250.  
  251. void
  252. ccleol(FILE *fp)
  253. {
  254.     struct save save;
  255.     
  256.     setup(cflush(fp), &save);
  257.     cleol();
  258.     restore(&save);
  259. }
  260.  
  261.  
  262. /*
  263.  *  csettabs - set tab stops
  264.  *
  265.  */
  266.  
  267. void
  268. csettabs(int tabs, FILE *fp)
  269. {
  270.     struct save save;
  271.     
  272.     setup(cflush(fp), &save);
  273.     if (tabs < 1 || tabs > c.ncols)
  274.         tabs = 1;
  275.     c.tabs = tabs;
  276.     restore(&save);
  277. }
  278.  
  279.  
  280. /*
  281.  *  csetmode - set console mode
  282.  *
  283.  */
  284.  
  285. void
  286. csetmode(int mode, FILE *fp)
  287. {
  288.     struct save save;
  289.  
  290.     setup(cflush(fp), &save);
  291.     c.raw = c.cbreak = c.edit = 0;
  292.     switch (mode) {
  293.         case C_RAW:
  294.             c.raw = 1;
  295.             break;
  296.         case C_CBREAK:
  297.             c.cbreak = 1;
  298.             break;
  299.         case C_NOECHO:
  300.             break;
  301.         case C_ECHO:
  302.             c.edit = 1;
  303.             break;
  304.     }
  305.     restore(&save);
  306. }
  307.  
  308.  
  309. /*
  310.  *  cinverse - set inverse video mode
  311.  *
  312.  *  As a side effect, the entire screen is erased.  The cursor is moved
  313.  *  to the start of its line.
  314.  *
  315.  */
  316.  
  317. void
  318. cinverse(int on, FILE *fp)
  319. {
  320.     register WindowPeek wp = cflush(fp);
  321.     struct save save;
  322.  
  323.     setup(wp, &save);
  324.     if (on) {
  325.         if (!wp->port.grafProcs) {
  326.             wp->port.grafProcs = malloc(sizeof(QDProcs));
  327.             SetStdProcs(wp->port.grafProcs);
  328.             wp->port.grafProcs->textProc = (Ptr) myStdText;
  329.         }
  330.     }
  331.     else {
  332.         if (wp->port.grafProcs) {
  333.             free(wp->port.grafProcs);
  334.             wp->port.grafProcs = 0;
  335.         }
  336.     }
  337.     cleos(0);
  338.     restore(&save);
  339. }
  340.  
  341.  
  342. /*
  343.  *  cshow - show a console window
  344.  *
  345.  *  All pending output to the window is forced to appear.
  346.  *
  347.  */
  348.  
  349. void
  350. cshow(FILE *fp)
  351. {
  352.     WindowPeek wp = cflush(fp);
  353.  
  354.     if (wp != (WindowPeek) FrontWindow())
  355.         SelectWindow(wp);
  356.     ShowWindow(wp);
  357. }
  358.  
  359.  
  360. /*
  361.  *  chide - hide a console window
  362.  *
  363.  */
  364.  
  365. void
  366. chide(FILE *fp)
  367. {
  368.     HideWindow(cflush(fp));
  369. }
  370.  
  371.  
  372. /*
  373.  *  cecho2file - echo console display to file
  374.  *
  375.  */
  376.  
  377. void
  378. cecho2file(char *s, int append, FILE *fp)
  379. {
  380.     struct save save;
  381.     
  382.     setup(cflush(fp), &save);
  383.     closeecho();
  384.     c.echo2fp = fopen(s, append ? "a" : "w");
  385.     c.spool = 0;
  386.     restore(&save);
  387. }
  388.  
  389.  
  390. /*
  391.  *  cecho2printer - echo console display to printer
  392.  *
  393.  */
  394.  
  395. void
  396. cecho2printer(FILE *fp)
  397. {
  398.     struct save save;
  399.     
  400.     setup(cflush(fp), &save);
  401.     closeecho();
  402.     c.echo2fp = tmpfile();
  403.     c.spool = 1;
  404.     restore(&save);
  405. }
  406.  
  407.  
  408. /* ---------- console management ---------- */
  409.  
  410.  
  411. /*
  412.  *  __open_std - open the std streams
  413.  *
  414.  *  This is called automatically (by "__checkfile") whenever an
  415.  *  unopened std stream is referenced.
  416.  *
  417.  */
  418.  
  419. void
  420. __open_std(void)
  421. {
  422.     FILE *fp = NULL;
  423.     char buf[40];
  424.     
  425.     if (stdin->std)
  426.         fp = freopenc(fp, stdin);
  427.     if (stdout->std)
  428.         fp = freopenc(fp, stdout);
  429.     if (stderr->std)
  430.         fp = freopenc(fp, stderr);
  431.     if (__log_stdout) {
  432.         sprintf(buf, "%#s.log", CurApName);
  433.         cecho2file(buf, 1, stdout);
  434.         console_options.pause_atexit = 0;
  435.     }
  436. }
  437.  
  438.  
  439. /*
  440.  *  InitConsole - initialize the console environment
  441.  *
  442.  */
  443.  
  444. static void
  445. InitConsole(void)
  446. {
  447.     MenuHandle menu;
  448.     int i;
  449.  
  450.         /*  initialize the Memory Manager  */
  451.     
  452.     if (ROM85 >= 0)
  453.         MaxApplZone();
  454.     for (i = 0; i < 10; i++)
  455.         MoreMasters();
  456.     
  457.         /*  initialize Quickdraw  */
  458.     
  459.     InitGraf(NewPtr(206) + 202);
  460.     
  461.         /*  initialize the Toolbox  */
  462.         
  463.     InitFonts();
  464.     InitWindows();
  465.     TEInit();
  466.     InitDialogs(0);
  467.     InitMenus();
  468.     
  469.         /*  create menus  */
  470.         
  471.     InsertMenu(appleMenu = NewMenu(1, "\p\024"), 0);
  472.     AppendMenu(appleMenu, "\pAbout Berkeley Logo;(-");
  473.     AddResMenu(appleMenu, 'DRVR');
  474.     InsertMenu(menu = NewMenu(2, "\pFile"), 0);
  475.     AppendMenu(menu, "\pQuit/Q");
  476.     InsertMenu(menu = NewMenu(3, "\pEdit"), 0);
  477.     AppendMenu(menu, "\pUndo/Z;(-;Cut/X;Copy/C;Paste/V;Clear");
  478.     DrawMenuBar();
  479.     
  480.         /*  ready to receive events  */
  481.         
  482.     FlushEvents(everyEvent, 0);
  483.     InitCursor();
  484.     console_environment = 1;
  485. }
  486.  
  487.  
  488. /*
  489.  *  ProcessEvent - handle one event
  490.  *
  491.  */
  492.  
  493. static void
  494. ProcessEvent(void)
  495. {
  496.     int key;
  497.     EventRecord event;
  498.     WindowPeek wp;
  499.     long choice;
  500.     Str255 buf;
  501.     DialogPtr myDialog;
  502.     
  503.         /*  process key from paste buffer  */
  504.  
  505.     if (c.pasteH) {
  506.         key = (unsigned char) (*c.pasteH)[c.pasteOfs++];
  507.         if (c.pasteOfs == c.pasteLen) {
  508.             DisposHandle(c.pasteH);
  509.             c.pasteH = 0;
  510.         }
  511.         if (c.inverse)
  512.             key &= 0x7F;
  513.         if (key == '\t')
  514.             key = ' ';
  515.         doKey(key);
  516.         return;
  517.     }
  518.     
  519.         /*  check for an event  */
  520.         
  521.     SystemTask();
  522.     SEvtEnb = false;
  523.     if (GetNextEvent(everyEvent, &event)) {
  524.         if (!SystemEvent(&event))
  525.             goto doEvent;
  526.     }
  527.     else if (event.what == nullEvent) {
  528.         if (FrontWindow() == 0)
  529.             InitCursor();
  530.     }
  531.     return;
  532.     
  533.         /*  handle event  */
  534.  
  535. doEvent:
  536.     if (event.what == mouseDown) {
  537.         switch (FindWindow(event.where, &wp)) {
  538.             case inMenuBar:
  539.                 InitCursor();
  540.                 choice = MenuSelect(event.where);
  541.                 goto doMenu;
  542.             case inSysWindow:
  543.                 SystemClick(&event, wp);
  544.                 break;
  545.         }
  546.     }
  547.     return;
  548.  
  549.         /*  handle menu choice  */
  550.  
  551. doMenu:    
  552.     switch (hiword(choice)) {
  553.         case 1:        /*  Apple  */
  554.             if (loword(choice) == 1) {
  555.                 myDialog = GetNewDialog(100,NULL,(WindowPtr)(-1L));
  556.                 ModalDialog(NULL,&choice);
  557.                 DisposDialog(myDialog);
  558.             }
  559.             else {
  560.                 GetItem(appleMenu, loword(choice), buf);
  561.                 OpenDeskAcc(buf);
  562.             }
  563.             break;
  564.         case 2:        /*  File  */
  565.             console_options.pause_atexit = 0;
  566.             exit(0);
  567.             /* no return */
  568.         case 3:        /*  Edit  */
  569.             SystemEdit(loword(choice) - 1);
  570.             break;
  571.     }
  572.     HiliteMenu(0);
  573. }
  574.  
  575.  
  576. /*
  577.  *  new_console - create a new console window
  578.  *
  579.  */
  580.  
  581. static WindowPeek
  582. new_console(void)
  583. {
  584.     GrafPtr savePort;
  585.     struct console **cH;
  586.     static Rect wbox = { 100, 100, 200, 300 };
  587.     register WindowPeek wp;
  588.     Rect bounds;
  589.     FontInfo fontInfo;
  590.     register WStateData *p;
  591.     
  592.     GetPort(&savePort);
  593.     use(0);
  594.     
  595.         /*  create the window  */
  596.         
  597.     wp = (WindowPeek) NewWindow(0, &wbox, console_options.title, 0, console_options.procID, (Ptr) -1, 0, 0);
  598.     MoveWindow(wp, console_options.left, console_options.top, 0);
  599.     SetPort(c.wp = wp);
  600.     
  601.         /*  set up the font characteristics  */
  602.         
  603.     TextFont(console_options.txFont);
  604.     TextSize(console_options.txSize);
  605.     TextFace(console_options.txFace);
  606.     GetFontInfo(&fontInfo);
  607.     c.height = fontInfo.ascent + fontInfo.descent + fontInfo.leading;
  608.     c.width = fontInfo.widMax;
  609.     
  610.         /*  set console defaults  */
  611.         
  612.     c.tabs = 8;
  613.     c.raw = c.cbreak = c.reading = c.inverse = 0;
  614.     c.edit = 1;
  615.     c.pasteH = 0;
  616.     c.echo2fp = 0;
  617.     
  618.         /*  set initial window size  */
  619.     
  620.     bounds.top = bounds.left = 0;
  621.     bounds.bottom = (c.nrows = console_options.nrows) * c.height + 8;
  622.     bounds.right = (c.ncols = console_options.ncols) * c.width + 8;
  623.     SizeWindow(wp, bounds.right, bounds.bottom, 0);
  624.     
  625.         /*  create TE record  */
  626.         
  627.     c.hTE = TENew(&bounds, &bounds);
  628.     (**c.hTE).crOnly = -1;
  629.     c.cursor.v = c.nrows - 1;
  630.     cleos(0);
  631.     
  632.         /*  set up grow/zoom parameters  */
  633.     
  634.     c.limit = botRight(bounds);
  635.     ++c.limit.v, ++c.limit.h;
  636.     LocalToGlobal(&topLeft(bounds));
  637.     LocalToGlobal(&botRight(bounds));
  638.     p = * (WStateData **) wp->dataHandle;
  639.     p->userState = p->stdState = bounds;
  640.     
  641.         /*  associate window with console  */
  642.     
  643.     asm {
  644.         lea        c,a0
  645.         move.l    #sizeof(c),d0
  646.         _PtrToHand
  647.         move.l    a0,offsetof(WindowRecord,refCon)(wp)
  648.     }
  649.     if (!console_refnum)
  650.         console_refnum = open_console_driver();
  651.     wp->windowKind = console_refnum;
  652.     
  653.         /*  done  */
  654.     
  655.     resize();
  656.     ShowWindow(wp);
  657.     SetPort(savePort);
  658.     return(wp);
  659. }
  660.  
  661.  
  662. /*
  663.  *  kill_console - close a console window
  664.  *
  665.  *  The console is not closed if more than one stream shares it.
  666.  *
  667.  */
  668.  
  669. static void
  670. kill_console(void)
  671. {
  672.     register FILE *fp;
  673.     int i = 0, n;
  674.     
  675.         /*  see if more than one stream shares this console  */
  676.         
  677.     for (fp = &__file[0], n = FOPEN_MAX; n--; fp++) {
  678.         if (fp->window == c.wp && i++)
  679.             return;
  680.     }
  681.     
  682.         /*  close echo file (if any)  */
  683.  
  684.     closeecho();
  685.     
  686.         /*  discard console data structures  */
  687.         
  688.     if (c.pasteH)
  689.         DisposHandle(c.pasteH);
  690.     DisposHandle((Handle) c.wp->refCon);
  691.     TEDispose(c.hTE);
  692.     DisposeWindow(c.wp);
  693.     c.wp = 0;
  694. }
  695.  
  696.  
  697. /*
  698.  *  closeecho - close echo file (if any)
  699.  *
  700.  */
  701.  
  702. static void
  703. closeecho(void)
  704. {
  705.     if (c.echo2fp) {
  706.         if (c.spool)
  707.             print_console();
  708.         fclose(c.echo2fp);
  709.     }
  710. }
  711.  
  712.  
  713. /*
  714.  *  cflush - flush all pending output to a console
  715.  *
  716.  */
  717.  
  718. static WindowPeek
  719. cflush(FILE *fp)
  720. {
  721.     WindowPeek wp = __checkfile(fp)->window;
  722.     int n;
  723.     
  724.     for (fp = &__file[0], n = FOPEN_MAX; n--; fp++) {
  725.         if (fp->dirty && fp->window == wp)
  726.             fflush(fp);
  727.     }
  728.     return(wp);
  729. }
  730.  
  731.  
  732. /* ---------- vectored entry points ---------- */
  733.  
  734.  
  735. /*
  736.  *  console_exit - console shutdown routine
  737.  *
  738.  */
  739.  
  740. static void
  741. console_exit(void)
  742. {
  743.     register FILE *fp;
  744.     int n;
  745.     
  746.         /*  complete pending output  */
  747.         
  748.     for (fp = &__file[0], n = FOPEN_MAX; n--; fp++) {
  749.         if (fp->dirty && fp->window)
  750.             fflush(fp);
  751.     }
  752.     
  753.         /*  pause for user acknowledgement  */
  754.     
  755.     if (console_environment && console_options.pause_atexit) {
  756.         for (fp = &__file[0], n = FOPEN_MAX; n--; fp++) {
  757.             if (fp->window) {
  758.                 SetWTitle(fp->window, "\ppress «return» to exit");
  759.                 c.raw = c.cbreak = c.edit = 0;
  760.                 setbuf(fp, NULL);
  761.                 fgetc(fp);
  762.                 break;
  763.             }
  764.         }
  765.     }
  766.     
  767.         /*  close consoles  */
  768.  
  769.     for (fp = &__file[0], n = FOPEN_MAX; n--; fp++) {
  770.         if (fp->window)
  771.             fclose(fp);
  772.     }
  773. }
  774.  
  775.  
  776. /*
  777.  *  consoleio - I/O handler proc for console windows
  778.  *
  779.  */
  780.  
  781. static int
  782. consoleio(FILE *fp, int i)
  783. {
  784.     struct save save;
  785.     int result = 0;
  786.     
  787.     if (_abnormal_exit)
  788.         return(0);
  789.     setup(fp->window, &save);
  790.     switch (i) {
  791.     
  792.                 /*  read  */
  793.             
  794.         case 0:
  795.             in.buf = in.ptr = fp->ptr;
  796.             if (console_environment) {
  797.                 cshow(fp);
  798.                 c.reading = 1;
  799.                 in.cnt = fp->cnt;
  800.                 if (c.edit && c.cursor.h + in.cnt > c.ncols)
  801.                     in.cnt = c.ncols - c.cursor.h + 1;
  802.                 in.min = in.max = c.raw ? 0 : setcursor(0);
  803.                 fp->eof = 0;
  804.                 do {
  805.                     ProcessEvent();
  806.                 } while (in.cnt && !c.raw);
  807.                 c.reading = 0;
  808.             }
  809.             if ((fp->cnt = in.ptr - in.buf) == 0) {
  810.                 fp->eof = 1;
  811.                 result = EOF;
  812.             }
  813.             break;
  814.             
  815.                 /*  write  */
  816.  
  817.         case 1:
  818.             output(fp->ptr, fp->cnt);
  819.             break;
  820.             
  821.                 /*  close  */
  822.  
  823.         case 2:
  824.             kill_console();
  825.             if (fp->window == save.console)
  826.                 save.console = 0;
  827.             break;
  828.     }
  829.     
  830.         /*  check for interrupt  */
  831.         
  832.     if (interrupted) {
  833.         interrupted = 0;
  834.         FlushEvents(keyDownMask, 0);
  835.         fp->cnt = 0;
  836.         raise(SIGINT);
  837.         errno = EINTR;
  838.         result = EOF;
  839.     }
  840.  
  841.     if (paused) {
  842.         paused = 0;
  843.         FlushEvents(keyDownMask, 0);
  844.         fp->cnt = 0;
  845.         raise(SIGABRT);
  846.         errno = EINTR;
  847.         result = EOF;
  848.     }
  849.  
  850.         /*  done  */
  851.     
  852.     restore(&save);
  853.     return(result);
  854. }
  855.  
  856.  
  857. /*
  858.  *  myStdText - inverse video handler
  859.  *
  860.  */
  861.  
  862. static pascal void
  863. myStdText(register short n, register char *s, Point numer, Point denom)
  864. {
  865.     register char *t;
  866.     char buf;
  867.     
  868.     while (n--) {
  869.         t = s;
  870.         asm {
  871. @1            tst.b    (s)+
  872. @2            dbmi    n,@1
  873.             bpl.s    @3
  874.             subq.l    #1,s
  875. @3        }
  876.         if (s > t)
  877.             StdText(s - t, t, numer, denom);
  878.         if (n < 0)
  879.             break;
  880.         buf = *s++ & 0x7F;
  881.         TextMode(notSrcCopy);
  882.         StdText(1, &buf, numer, denom);
  883.         TextMode(srcCopy);
  884.     }
  885. }
  886.  
  887.  
  888. /* ---------- I/O primitives ---------- */
  889.  
  890.  
  891. /*
  892.  *  cleos - clear to end of screen
  893.  *
  894.  *  The given line, and all following lines, are erased.  The cursor is
  895.  *  moved to the start of its line.
  896.  *
  897.  */
  898.  
  899. static void
  900. cleos(int i)
  901. {
  902.     pad('\r', 0, c.nrows - i);
  903.     paste((**c.hTE).lineStarts[i], (**c.hTE).teLength);
  904.     c.cursor.h = 0;
  905. }
  906.  
  907.  
  908. /*
  909.  *  cleol - clear to end of line
  910.  *
  911.  */
  912.  
  913. static void
  914. cleol(void)
  915. {
  916.     register TEPtr pTE = deactivate();
  917.     register short *line = &pTE->lineStarts[c.cursor.v];
  918.     register int selStart = line[0] + c.cursor.h;
  919.     register int selEnd = line[1] - 1;
  920.  
  921.     if (selStart < selEnd) {
  922.         pTE->selStart = selStart;
  923.         pTE->selEnd = selEnd;
  924.         TEDelete(c.hTE);
  925.     }
  926. }
  927.  
  928.  
  929. /*
  930.  *  output - write characters to the current console
  931.  *
  932.  */
  933.  
  934. static void
  935. output(register unsigned char *s, register short n)
  936. {
  937.     unsigned char *t;
  938.     register EvQElPtr q;
  939.     
  940.     while (n--) {
  941.         t = s;
  942.         asm {
  943.             moveq    #' ',d0
  944. @1            cmp.b    (s)+,d0
  945. @2            dbhi    n,@1
  946.             bls.s    @3
  947.             subq.l    #1,s
  948. @3        }
  949.         if (s > t)
  950.             overwrite(t, s - t);
  951.         if (n < 0)
  952.             break;
  953.         if (!c.raw) {
  954.             for (q = (EvQElPtr) EventQueue.qHead; q; q = (EvQElPtr) q->qLink) {
  955.                 if (q->evtQWhat == keyDown && (char) q->evtQMessage == '.') {
  956.                     if (q->evtQModifiers & cmdKey) {
  957.                         interrupted = 1;
  958.                         return;
  959.                     }
  960.                 }
  961.             }
  962.             for (q = (EvQElPtr) EventQueue.qHead; q; q = (EvQElPtr) q->qLink) {
  963.                 if (q->evtQWhat == keyDown && (char) q->evtQMessage == ',') {
  964.                     if (q->evtQModifiers & cmdKey) {
  965.                         paused = 1;
  966.                         return;
  967.                     }
  968.                 }
  969.             }
  970.         }
  971.         switch (*s++) {
  972.             case '\a':
  973.                 SysBeep(4);
  974.                 break;
  975.             case '\b':
  976.                 deactivate();
  977.                 if (c.cursor.h)
  978.                     --c.cursor.h;
  979.                 break;
  980.             case '\f':
  981.                 c.cursor.v = 0;
  982.                 cleos(0);
  983.                 break;
  984.             case '\n':
  985.                 newline();
  986.                 break;
  987.             case '\v':
  988.                 if (++c.cursor.v == c.nrows)
  989.                     --c.cursor.v;
  990.                 break;
  991.             case '\r':
  992.                 c.cursor.h = 0;
  993.                 break;
  994.             case '\t':
  995.                 do {
  996.                     ++c.cursor.h;
  997.                 } while (c.cursor.h % c.tabs);
  998.                 if (c.cursor.h > c.ncols)
  999.                     c.cursor.h = c.ncols;
  1000.                 break;
  1001.         }
  1002.     }
  1003. }
  1004.  
  1005.  
  1006. /*
  1007.  *  overwrite - place new text on a line
  1008.  *
  1009.  *  The text must not contain any control characters.  Text is drawn
  1010.  *  starting at the current cursor, overwriting any existing characters.
  1011.  *  The cursor is left at the end of the new text.
  1012.  *
  1013.  */
  1014.  
  1015. static void
  1016. overwrite(unsigned char *s, long n)
  1017. {
  1018.     register long m;
  1019.     register short *line, selStart, selEnd;
  1020.     
  1021.         /*  wrap output at "ncols"  */
  1022.  
  1023. more:    
  1024.     if (c.cursor.h + (m = n) > c.ncols)
  1025.         m = c.ncols - c.cursor.h;
  1026.     
  1027.         /*  set replacement range  */
  1028.         
  1029.     line = &(**c.hTE).lineStarts[c.cursor.v];
  1030.     selStart = line[0] + c.cursor.h;
  1031.     selEnd = line[1] - 1;
  1032.     if (selStart > selEnd) {    /*  pad line with blanks  */
  1033.         pad(' ', 0, selStart - selEnd);
  1034.         paste(selEnd, selEnd);
  1035.         selEnd = selStart;
  1036.     }
  1037.     else if (selEnd > selStart + m)
  1038.         selEnd = selStart + m;
  1039.     
  1040.         /*  paste in new text  */
  1041.     
  1042.     PtrToXHand(s, TEScrpHandle, m);
  1043.     TEScrpLength = m;
  1044.     paste(selStart, selEnd);
  1045.     
  1046.         /*  wrap to next line if necessary  */
  1047.         
  1048.     if (m < n) {
  1049.         newline();
  1050.         s += m;
  1051.         n -= m;
  1052.         goto more;
  1053.     }
  1054.     c.cursor.h += m;
  1055. }
  1056.  
  1057.  
  1058. /*
  1059.  *  blank - erase the indicated portion of the input field
  1060.  *
  1061.  */
  1062.  
  1063. static void
  1064. blank(int selStart, int selEnd)
  1065. {
  1066.     register TEPtr pTE = deactivate();
  1067.     
  1068.     if (in.max + 1 == pTE->lineStarts[c.cursor.v + 1]) {
  1069.         pTE->selStart = selStart;
  1070.         pTE->selEnd = selEnd;
  1071.         TEDelete(c.hTE);
  1072.     }
  1073.     else {
  1074.         pTE->selStart = selEnd;
  1075.         pTE->selEnd = in.max;
  1076.         TECopy(c.hTE);
  1077.         pad(' ', in.max - selEnd, in.max - selStart);
  1078.         paste(selStart, in.max);
  1079.     }
  1080.     in.max -= selEnd - selStart;
  1081. }
  1082.  
  1083.  
  1084. /*
  1085.  *  insert - insert a character at the indicated point
  1086.  *
  1087.  */
  1088.  
  1089. static void
  1090. insert(char ch, int selStart)
  1091. {
  1092.     register TEPtr pTE = deactivate();
  1093.     
  1094.     pTE->selStart = selStart;
  1095.     if (in.max + 1 == pTE->lineStarts[c.cursor.v + 1]) {
  1096.         pTE->selEnd = selStart;
  1097.         TEKey(ch, c.hTE);
  1098.     }
  1099.     else {
  1100.         pTE->selEnd = in.max;
  1101.         TECopy(c.hTE);
  1102.         Munger(TEScrpHandle, 0, 0, 0, &ch, 1);
  1103.         ++TEScrpLength;
  1104.         paste(selStart, in.max + 1);
  1105.     }
  1106.     in.max++;
  1107. }
  1108.  
  1109.  
  1110. /*
  1111.  *  pad - pad the TE scrap to a desired size
  1112.  *
  1113.  */
  1114.  
  1115. static void
  1116. pad(register char ch, register long ofs, register long len)
  1117. {
  1118.         /*  set scrap size  */
  1119.         
  1120.     asm {
  1121.         movea.l    TEScrpHandle,a0
  1122.         move.l    len,d0
  1123.         move.w    d0,TEScrpLength
  1124.         _SetHandleSize
  1125.     }
  1126.     
  1127.         /*  fill scrap  */
  1128.         
  1129.     asm {
  1130.         movea.l    (a0),a0
  1131.         adda.l    ofs,a0
  1132.         sub.l    ofs,len
  1133.         bra.s    @2
  1134. @1        move.b    ch,(a0)+
  1135. @2        dbra    len,@1
  1136.     }
  1137. }
  1138.  
  1139.  
  1140. /*
  1141.  *  paste - replace range with contents of TEScrap
  1142.  *
  1143.  */
  1144.  
  1145. static void
  1146. paste(int selStart, int selEnd)
  1147. {
  1148.     register TEPtr pTE = deactivate();
  1149.     
  1150.     pTE->selStart = selStart;
  1151.     pTE->selEnd = selEnd;
  1152.     TEPaste(c.hTE);
  1153. }
  1154.  
  1155.  
  1156. /*
  1157.  *  setcursor - activate flashing caret at cursor
  1158.  *
  1159.  */
  1160.  
  1161. static int
  1162. setcursor(int sel)
  1163. {
  1164.     register TEPtr pTE = deactivate();
  1165.     register short *line = &pTE->lineStarts[c.cursor.v];
  1166.     register int end = line[1] - 1;
  1167.     
  1168.     sel += line[0] + c.cursor.h;
  1169.     if (sel > end) {        /*  pad line with blanks  */
  1170.         pad(' ', 0, sel - end);
  1171.         paste(end, end);
  1172.         pTE = *c.hTE;
  1173.     }
  1174.     pTE->selStart = pTE->selEnd = sel;
  1175.     pTE->clikStuff = 255;    /* per TN127 */
  1176.     TEActivate(c.hTE);
  1177.     return(sel);
  1178. }
  1179.  
  1180.  
  1181. /*
  1182.  *  deactivate - make sure TE record is inactive
  1183.  *
  1184.  *  The dereferenced pointer to the TE record is returned.
  1185.  *
  1186.  */
  1187.  
  1188. static TEPtr
  1189. deactivate(void)
  1190. {
  1191.     if ((**c.hTE).active)
  1192.         TEDeactivate(c.hTE);
  1193.     return(*c.hTE);
  1194. }
  1195.  
  1196.  
  1197. /*
  1198.  *  strip - strip trailing blanks from all lines
  1199.  *
  1200.  *  Note that we don't strip blanks that are part of the current
  1201.  *  input field.
  1202.  *
  1203.  */
  1204.  
  1205. static void
  1206. strip(void)
  1207. {
  1208.     register int i = c.nrows, j, k;
  1209.     register TEPtr pTE = *c.hTE;
  1210.     register char *p;
  1211.     
  1212.     while (i) {
  1213.         j = k = pTE->lineStarts[i--] - 1;
  1214.         for (p = *pTE->hText + j; j && *--p == ' '; j--)
  1215.             ;
  1216.         if (c.reading && !c.raw && i == c.cursor.v && j < in.max)
  1217.             j = in.max;
  1218.         if (k -= j) {
  1219.             Munger(pTE->hText, j, 0, k, "", 0);
  1220.             pTE = *c.hTE;
  1221.             if (c.reading) {
  1222.                 if (in.min > j)
  1223.                     in.min -= k;
  1224.                 if (in.max > j)
  1225.                     in.max -= k;
  1226.             }
  1227.             if (pTE->selStart > j)
  1228.                 pTE->selStart -= k;
  1229.             if (pTE->selEnd > j)
  1230.                 pTE->selEnd -= k;
  1231.         }
  1232.     }
  1233.     TECalText(c.hTE);
  1234. }
  1235.  
  1236.  
  1237. /*
  1238.  *  resize - respond to new window size
  1239.  *
  1240.  *  If the window is too small to display the entire console, the lower
  1241.  *  left portion of the console is displayed.
  1242.  *
  1243.  */
  1244.  
  1245. static void
  1246. resize(void)
  1247. {
  1248.     Rect bounds;
  1249.     
  1250.     bounds = c.wp->port.portRect;
  1251.     InvalRect(&bounds);
  1252.     InsetRect(&bounds, 4, 4);
  1253.     (**c.hTE).viewRect = bounds;
  1254.     bounds.top = bounds.bottom - c.height * c.nrows;
  1255.     (**c.hTE).destRect = bounds;
  1256. }
  1257.  
  1258.  
  1259. /*
  1260.  *  setup - focus on the appropriate console
  1261.  *
  1262.  *  The current console and the current grafport are set appropriately,
  1263.  *  and the previous values are saved.  Any pending update is performed.
  1264.  *
  1265.  */
  1266.  
  1267. static void
  1268. setup(WindowPeek wp, struct save *save)
  1269. {
  1270.     Rect bounds;
  1271.     
  1272.     GetPort(&save->port);
  1273.     save->console = theConsole;
  1274.     if (wp && wp->windowKind == console_refnum) {
  1275.         use(wp);
  1276.         SetPort(wp);
  1277.         if (!EmptyRgn(wp->updateRgn)) {
  1278.             bounds = wp->port.portRect;
  1279.             BeginUpdate(wp);
  1280.             EraseRect(&bounds);
  1281.             TEUpdate(&bounds, c.hTE);
  1282.             if (wp == graphics_window)    /* mak */
  1283.                 redraw_graphics();
  1284.             EndUpdate(wp);
  1285.         }
  1286.         theConsole = wp;
  1287.     }
  1288. }
  1289.  
  1290.  
  1291. /*
  1292.  *  restore - done with the current console
  1293.  *
  1294.  *  The console and grafport saved by a previous "setup" call are restored.
  1295.  *
  1296.  */
  1297.  
  1298. static void
  1299. restore(struct save *save)
  1300. {
  1301.     if (theConsole = save->console)
  1302.         use(save->console);
  1303.     SetPort(save->port);
  1304. }
  1305.  
  1306.  
  1307. /*
  1308.  *  use - load global "c" from window's refCon
  1309.  *
  1310.  */
  1311.  
  1312. static void
  1313. use(WindowPeek wp)
  1314. {
  1315.     if (wp != c.wp) {
  1316.         if (c.wp)
  1317.             ** (struct console **) c.wp->refCon = c;
  1318.         if (wp)
  1319.             c = ** (struct console **) wp->refCon;
  1320.     }
  1321. }
  1322.  
  1323.  
  1324. /*
  1325.  *  copy - get a pointer to the text in the TE scrap
  1326.  *
  1327.  *  If inverse video is in use, the high bit is stripped from each byte.
  1328.  *  This call must be balanced by a call to "endcopy".
  1329.  *
  1330.  */
  1331.  
  1332. static char *
  1333. copy(void)
  1334. {
  1335.     asm {
  1336.         movea.l    TEScrpHandle,a0
  1337.         _HLock
  1338.         move.l    (a0),d0
  1339.     }
  1340.     if (c.inverse) asm {
  1341.         movea.l    d0,a1
  1342.         move.w    TEScrpLength,d1
  1343.         bra.s    @2
  1344. @1        tst.b    (a1)+
  1345.         bpl.s    @2
  1346.         bclr    #7,-1(a1)
  1347. @2        dbra    d1,@1
  1348.     }
  1349. }
  1350.  
  1351.  
  1352. /*
  1353.  *  endcopy - done using text obtained by "copy"
  1354.  *
  1355.  */
  1356.  
  1357. static void
  1358. endcopy(void)
  1359. {
  1360.     HUnlock(TEScrpHandle);
  1361. }
  1362.  
  1363.  
  1364. /*
  1365.  *  newline - print a newline character
  1366.  *
  1367.  */
  1368.  
  1369. static void
  1370. newline(void)
  1371. {
  1372.     register TEPtr pTE = deactivate();
  1373.     register short *line, delta, len;
  1374.     Rect bounds;
  1375.     RgnHandle rgn;
  1376.     Handle hText;
  1377.     EventRecord event;
  1378.     
  1379.     if (c.reading && !c.edit && !c.cbreak)
  1380.         return;
  1381.  
  1382.         /*  wait while mouse is down  */
  1383.  
  1384.     if (GetOSEvent(mDownMask, &event)) {
  1385.         while (!GetOSEvent(mUpMask, &event))
  1386.             ;
  1387.     }
  1388.     
  1389.         /*  copy current line to echo-file  */
  1390.         
  1391.     if (c.echo2fp) {
  1392.         line = &pTE->lineStarts[c.cursor.v];
  1393.         pTE->selStart = line[0];
  1394.         pTE->selEnd = line[1];
  1395.         TECopy(c.hTE);
  1396.         fwrite(copy(), 1, TEScrpLength, c.echo2fp);
  1397.         endcopy();
  1398.     }
  1399.  
  1400.         /*  advance cursor, scrolling if necessary  */
  1401.  
  1402.     if (++c.cursor.v == c.nrows) {
  1403.         pTE = *c.hTE;
  1404.         hText = pTE->hText;
  1405.         len = pTE->teLength -= delta = pTE->lineStarts[1];
  1406.         ++pTE->teLength;
  1407.         bounds = pTE->destRect;
  1408.         ScrollRect(&bounds, 0, -c.height, rgn = NewRgn());
  1409.         DisposeRgn(rgn);
  1410.         Munger(hText, 0, 0, delta, "", 0);
  1411.         Munger(hText, len, 0, 0, "\r", 1);
  1412.         TECalText(c.hTE);
  1413.         --c.cursor.v;
  1414.     }
  1415.     c.cursor.h = 0;
  1416. }
  1417.  
  1418.  
  1419. /* ---------- console driver ---------- */
  1420.  
  1421.  
  1422. /*
  1423.  *  open_console_driver - install and initialize the console driver
  1424.  *
  1425.  */
  1426.  
  1427. static int
  1428. open_console_driver(void)
  1429. {
  1430.     short i;
  1431.     DCtlHandle dceH;
  1432.     register DCtlPtr dce;
  1433.     
  1434.         /*  create driver on heap  */
  1435.     
  1436.     if (!drvrH) {
  1437.         drvr.vCtl.vCode = doControl;
  1438.         drvr.vClose.vCode = doClose;
  1439.         asm {
  1440.             lea        drvr,a0
  1441.             move.l    #sizeof(drvr),d0
  1442.             _PtrToHand
  1443.             move.l    a0,drvrH
  1444.         }
  1445.     }
  1446.     
  1447.         /*  find available slot in unit table  */
  1448.         
  1449.     for (i = 27; dceH = UTableBase[i]; i++) {
  1450.         if (!((**dceH).dCtlFlags & dOpened))
  1451.             break;
  1452.     }
  1453.     i = ~i;
  1454.     
  1455.         /*  create DCE  */
  1456.         
  1457.     asm {
  1458.         move.w    i,d0
  1459.         dc.w    0xA13D            ;  _DrvrInstall
  1460.         movea.l    (a0),dce
  1461.     }
  1462.     dce->dCtlDriver = (Ptr) drvrH;
  1463.     dce->dCtlFlags = drvr.drvrFlags;
  1464.     dce->dCtlEMask = drvr.drvrEMask;
  1465.     return(i);
  1466. }
  1467.  
  1468.  
  1469. /*
  1470.  *  doClose - handle _PBClose call
  1471.  *
  1472.  *  Normally, we shouldn't get a close call, because a console window has
  1473.  *  no go-away box.  If we do reach here, we can keep from crashing (except
  1474.  *  on 64K ROMs) by refusing to close.
  1475.  *
  1476.  */
  1477.  
  1478. static int
  1479. doClose(void)
  1480. {
  1481.     return(closErr);
  1482. }
  1483.  
  1484.  
  1485. /*
  1486.  *  doControl - handle _PBControl call
  1487.  *
  1488.  */
  1489.  
  1490. static int
  1491. doControl(void)
  1492. {
  1493.     register CntrlParam *pb;
  1494.     DCtlPtr dce;
  1495.     struct save save, save2;
  1496.     register EventRecord *event;
  1497.     int key;
  1498.     long oldA5 = SetCurrentA5();
  1499.     
  1500.     asm {
  1501.         move.l    a0,pb
  1502.         move.l    a1,dce
  1503.     }
  1504.     setup((WindowPeek) FrontWindow(), &save);
  1505.     switch (pb->csCode) {
  1506.         case accCursor:
  1507.             doCursor();
  1508.             break;
  1509.         case accCut:
  1510.             doCut();
  1511.             break;
  1512.         case accCopy:
  1513.             doCopy();
  1514.             break;
  1515.         case accPaste:
  1516.             doPaste();
  1517.             break;
  1518.         case accClear:
  1519.             doKey('\33');
  1520.             break;
  1521.         case accEvent:
  1522.             event = * (EventRecord **) pb->csParam;
  1523.             switch (event->what) {
  1524.                 case updateEvt:
  1525.                     setup((WindowPeek) event->message, &save2);
  1526.                     break;
  1527.                 case mouseDown:
  1528.                     doClick(event);
  1529.                     break;
  1530.                 case keyDown:
  1531.                 case autoKey:
  1532.                     key = (unsigned char) event->message;
  1533.                     if (event->modifiers & cmdKey) {
  1534.                         if (event->what == autoKey)
  1535.                             break;
  1536.                         key = doCmdKey(key);
  1537.                     }
  1538.                     if (key)
  1539.                         doKey(key);
  1540.                     break;
  1541.             }
  1542.             break;
  1543.     }
  1544.     HUnlock(drvrH);
  1545.     HUnlock(RecoverHandleSys(dce));
  1546.     restore(&save);
  1547.     SetA5(oldA5);
  1548.     return(0);
  1549. }
  1550.  
  1551.  
  1552. /*
  1553.  *  doCursor - cursor maintenance
  1554.  *
  1555.  */
  1556.  
  1557. static void
  1558. doCursor(void)
  1559. {
  1560.     Point where;
  1561.     
  1562.     TEIdle(c.hTE);
  1563.     GetMouse(&where);
  1564.     if (PtInRect(where, &c.wp->port.portRect))
  1565.         SetCursor(*GetCursor(iBeamCursor));
  1566.     else asm {
  1567.         movea.l    (a5),a0
  1568.         pea        -108(a0)            ;  arrow
  1569.         _SetCursor
  1570.     }
  1571. }
  1572.  
  1573.  
  1574. /*
  1575.  *  doClick - handle mouse-down event
  1576.  *
  1577.  *  Since the click was passed to the console driver, the front window
  1578.  *  must be a console window, and the click can only be in its zoom box
  1579.  *  or content region (including the grow region).
  1580.  *
  1581.  */
  1582.  
  1583. static void
  1584. doClick(EventRecord *event)
  1585. {
  1586.     int part;
  1587.     
  1588.     c.wp->windowKind = userKind;
  1589.     part = FindWindow(event->where, &c.wp);
  1590.     c.wp->windowKind = console_refnum;
  1591.     switch (part) {
  1592.         case inZoomIn:
  1593.         case inZoomOut:
  1594.             doZoom(event->where, part);
  1595.             break;
  1596.         case inGrow:
  1597.             if (!(event->modifiers & (cmdKey|optionKey))) {
  1598.                 doGrow(event->where);
  1599.                 break;
  1600.             }
  1601.             /* ... */
  1602.         case inContent:
  1603.             doSelect(event);
  1604.             break;
  1605.     }
  1606. }
  1607.  
  1608.  
  1609. /*
  1610.  *  doZoom - handle click in zoom box
  1611.  *
  1612.  *  The "zoomed" size is that needed to display the entire console.
  1613.  *
  1614.  */
  1615.  
  1616. static void
  1617. doZoom(Point where, int part)
  1618. {
  1619.     register WindowPeek wp = c.wp;
  1620.  
  1621.     InitCursor();
  1622.     if (TrackBox(wp, where, part)) {
  1623.         EraseRect(&wp->port.portRect);
  1624.         ZoomWindow(wp, part, 0);
  1625.         resize();
  1626.     }
  1627. }
  1628.  
  1629.  
  1630. /*
  1631.  *  doGrow - handle click in grow region
  1632.  *
  1633.  *  No grow box is actually visible; nonetheless, clicking in the lower
  1634.  *  right corner has the same effect.  Hold down the command or option
  1635.  *  key to defeat this behavior (e.g. to select corner text).
  1636.  *
  1637.  *  The maximum window size is that needed to display the entire console.
  1638.  *
  1639.  */
  1640.  
  1641. static void
  1642. doGrow(Point where)
  1643. {
  1644.     register WindowPeek wp = c.wp;
  1645.     long size;
  1646.     static Rect limit = { 76, 120, 9999, 9999 };   /* mak */
  1647.     int old_right, old_bottom;   /* mak */
  1648.     
  1649.     InitCursor();
  1650.     botRight(limit) = c.limit;
  1651.     if (size = GrowWindow(wp, where, &limit)) {
  1652.         EraseRect(&wp->port.portRect);
  1653.         if (wp == graphics_window) {
  1654.             old_right = graphics_window->portRect.right;
  1655.             old_bottom = graphics_window->portRect.bottom;
  1656.         }
  1657.     
  1658.         SizeWindow(wp, (loword(size) | 1) - 1, (hiword(size) | 1) - 1, 0);   /* mak */
  1659.         resize();
  1660.         if (wp == graphics_window)   /* mak */
  1661.             resize_record((graphics_window->portRect.right - old_right) / 2,
  1662.                           (graphics_window->portRect.bottom - old_bottom) / 2);
  1663.     }
  1664. }
  1665.  
  1666.  
  1667. /*
  1668.  *  doSelect - handle click in content region, initiating selection
  1669.  *
  1670.  */
  1671.  
  1672. static void
  1673. doSelect(EventRecord *event)
  1674. {
  1675.     int extend = 0;
  1676.     register TEPtr pTE;
  1677.     
  1678.     if (!(**c.hTE).active)
  1679.         setcursor(0);
  1680.     else if (event->modifiers & shiftKey)
  1681.         extend = 1;
  1682.     strip();
  1683.     
  1684.         /*  let TE do the work  */
  1685.         
  1686.     GlobalToLocal(&event->where);
  1687.     TEClick(event->where, extend, c.hTE);
  1688.     pTE = *c.hTE;
  1689.     
  1690.         /*  fix selection  */
  1691.         
  1692.     if (pTE->selStart == pTE->selEnd) {
  1693.         pTE->clikStuff = 255;    /* per TN127 */
  1694.         if (!c.reading || c.raw)
  1695.             TEDeactivate(c.hTE);
  1696.         else if (pTE->selStart < in.min)
  1697.             TESetSelect(in.min, in.min, c.hTE);
  1698.         else if (pTE->selEnd > in.max)
  1699.             TESetSelect(in.max, in.max, c.hTE);
  1700.     }
  1701. }
  1702.  
  1703.  
  1704. /*
  1705.  *  doCmdKey - handle command key
  1706.  *
  1707.  */
  1708.  
  1709. static int
  1710. doCmdKey(int key)
  1711. {
  1712.     if (c.raw)
  1713.         return(key & 0x1F);        /*  controlify  */
  1714.     switch (key) {
  1715.         case 'x': case 'X':
  1716.             doCut();
  1717.             break;
  1718.         case 'c': case 'C':
  1719.             doCopy();
  1720.             break;
  1721.         case 'v': case 'V':
  1722.             doPaste();
  1723.             break;
  1724.         case '.':
  1725.             if (console_environment)
  1726.                 interrupted = 1;
  1727.         case ',':
  1728.             if (console_environment)
  1729.                 paused = 1;
  1730.             /* ... */
  1731.         case 'd': case 'D':
  1732.             return('\4');        /*  ctrl-D  */
  1733.         case 'u': case 'U':
  1734.         case 'z': case 'Z':
  1735.             return('\25');        /*  ctrl-U  */
  1736.         case 'q': case 'Q':
  1737.             if (console_environment) {
  1738.                 console_options.pause_atexit = 0;
  1739.                 exit(0);
  1740.             }
  1741.             break;
  1742.     }
  1743.     return(0);
  1744. }
  1745.  
  1746.  
  1747. /*
  1748.  *  doKey - handle keypress
  1749.  *
  1750.  */
  1751.  
  1752. static void
  1753. doKey(int key)
  1754. {
  1755.     register TEPtr pTE = *c.hTE;
  1756.     register int selStart = pTE->selStart;
  1757.     register int selEnd = pTE->selEnd;
  1758.     register short len;
  1759.     register char *p;
  1760.  
  1761.     if (!c.reading || c.inverse && key > 0x7F)
  1762.         goto beep;
  1763.     if (c.raw) {
  1764.         *in.ptr++ = key;
  1765.         in.cnt = 0;
  1766.         return;
  1767.     }
  1768.     
  1769.         /*  process control characters  */
  1770.         
  1771.     if (key < ' ') {
  1772.         switch (key) {
  1773.             case '\25':                        /*  ^U  */
  1774.             case '\33':                        /*  escape, clear  */
  1775.                 in.cnt += in.ptr - in.buf;
  1776.                 in.ptr = in.buf;
  1777.                 selStart = in.min;
  1778.                 selEnd = in.max;
  1779.                 goto blank;
  1780.             case '\b':                        /*  backspace  */
  1781.                 if (c.edit)
  1782.                     goto blank;
  1783.                 if (c.cbreak)
  1784.                     goto buffer;
  1785.                 if (in.ptr == in.buf)
  1786.                     goto beep;
  1787.                 --in.ptr;
  1788.                 ++in.cnt;
  1789.                 goto cursor;
  1790.             case '\34':                        /*  left arrow  */
  1791.                 if (selStart == selEnd)
  1792.                     --selStart;
  1793.                 goto cursor;
  1794.             case '\35':                        /*  right arrow  */
  1795.                 if (selStart == selEnd)
  1796.                     ++selEnd;
  1797.                 selStart = selEnd;
  1798.                 goto cursor;
  1799.             case '\36':                        /*  up arrow  */
  1800.                 selStart = in.min;
  1801.                 goto cursor;
  1802.             case '\37':                        /*  down arrow  */
  1803.             case '\t':                        /*  tab  */
  1804.                 selStart = in.max;
  1805.                 goto cursor;
  1806.             case '\4':                        /*  ^D  */
  1807.             case '\r':                        /*  return  */
  1808.             case '\3':                        /*  enter  */
  1809.                 if (len = in.max - in.min) {
  1810.                     p = *pTE->hText + in.min;
  1811.                     asm {
  1812.                         movea.l    in.ptr,a0
  1813.                         bra.s    @2
  1814. @1                        move.b    (p)+,(a0)+
  1815. @2                        dbra    len,@1
  1816.                         move.l    a0,in.ptr
  1817.                     }
  1818.                 }
  1819.                 if (key != '\4')
  1820.                     *in.ptr++ = '\n';
  1821.                 newline();
  1822.                 in.cnt = 0;
  1823.                 break;
  1824.         }
  1825.         return;
  1826.     }
  1827.     
  1828.         /*  delete any existing selection  */
  1829.  
  1830. blank:
  1831.     if (c.edit) {
  1832.         if (selStart == selEnd) {
  1833.             if (key != '\b')
  1834.                 goto insert;
  1835.             --selStart;
  1836.         }
  1837.         if (selStart < in.min || selEnd > in.max)
  1838.             goto beep;
  1839.         blank(selStart, selEnd);
  1840.     }
  1841.  
  1842.         /*  insert the key  */
  1843.         
  1844. insert:
  1845.     if (key >= ' ') {
  1846.         if (in.max - in.min == in.cnt - 1)
  1847.             SysBeep(2);
  1848.         else if (c.edit)
  1849.             insert(key, selStart++);
  1850.         else {
  1851. buffer:        *in.ptr++ = key;
  1852.             if (c.cbreak) {
  1853.                 output(in.ptr - 1, 1);
  1854.                 in.cnt = 0;
  1855.                 return;
  1856.             }
  1857.             --in.cnt;
  1858.         }
  1859.     }
  1860.     
  1861.         /*  update the cursor  */
  1862.  
  1863. cursor:
  1864.     if (selStart > in.max)
  1865.         selStart = in.max;
  1866.     else if (selStart < in.min)
  1867.         selStart = in.min;
  1868.     setcursor(selStart - in.min);
  1869.     return;
  1870.     
  1871.         /*  beep only  */
  1872.  
  1873. beep:
  1874.     SysBeep(2);
  1875. }
  1876.  
  1877.  
  1878. /*
  1879.  *  doCut - handle "cut" command
  1880.  *
  1881.  *  Selected text (if any) is placed in the desk scrap, then deleted.
  1882.  *
  1883.  */
  1884.  
  1885. static void
  1886. doCut(void)
  1887. {
  1888.     register TEPtr pTE = *c.hTE;
  1889.     
  1890.     if (pTE->active && pTE->selStart < pTE->selEnd) {
  1891.         if (!c.reading || pTE->selStart < in.min || pTE->selEnd > in.max)
  1892.             SysBeep(2);
  1893.         else {
  1894.             doCopy();
  1895.             doKey('\b');
  1896.         }
  1897.     }
  1898. }
  1899.  
  1900.  
  1901. /*
  1902.  *  doCopy - handle "copy" command
  1903.  *
  1904.  *  Selected text (if any) is placed in the desk scrap.
  1905.  *
  1906.  */
  1907.  
  1908. static void
  1909. doCopy(void)
  1910. {
  1911.     register TEPtr pTE = *c.hTE;
  1912.     
  1913.     if (pTE->active && pTE->selStart < pTE->selEnd) {
  1914.         TECopy(c.hTE);
  1915.         ZeroScrap();
  1916.         PutScrap(TEScrpLength, 'TEXT', copy());
  1917.         endcopy();
  1918.     }
  1919. }
  1920.  
  1921.  
  1922. /*
  1923.  *  doPaste - handle "paste" command
  1924.  *
  1925.  *  Pasted text is obtained from the desk scrap and stored in the paste
  1926.  *  buffer, "c.pasteH".  ProcessEvent will take characters from the buffer
  1927.  *  in preference to the keyboard.
  1928.  *
  1929.  */
  1930.  
  1931. static void
  1932. doPaste(void)
  1933. {
  1934.     if (!c.reading || (**c.hTE).selStart < in.min || (**c.hTE).selEnd > in.max) {
  1935.         SysBeep(2);
  1936.         return;
  1937.     }
  1938.     if ((c.pasteLen = GetScrap(TEScrpHandle, 'TEXT', &c.pasteOfs)) > 0) {
  1939.         c.pasteH = TEScrpHandle;
  1940.         TEScrpHandle = NewHandle(0);
  1941.         c.pasteOfs = 0;
  1942.     }
  1943.     TEScrpLength = 0;
  1944. }
  1945.  
  1946.  
  1947. /* ---------- printing ---------- */
  1948.  
  1949.  
  1950. /*
  1951.  *  print_console - print the echo file
  1952.  *
  1953.  */
  1954.  
  1955. static void
  1956. print_console(void)
  1957. {
  1958.         /*  check for availability of printing  */
  1959.  
  1960. #ifdef __PRINTTRAPS__
  1961.     if (GetTrapAddress(0xA8FD) == GetTrapAddress(0xA89F)) {
  1962.         c.echo2fp->remove = 0;
  1963.         return;
  1964.     }
  1965. #endif
  1966.     
  1967.         /*  print away  */
  1968.         
  1969.     if (!noPrint) {
  1970.         PrOpen();
  1971.         if (!PrError()) {
  1972.             print();
  1973.             PrClose();
  1974.         }
  1975.     }
  1976. }
  1977.  
  1978.  
  1979. /*
  1980.  *  print - spool file to printer
  1981.  *
  1982.  */
  1983.  
  1984. static void
  1985. print(void)
  1986. {
  1987.     static THPrint hPrint;
  1988.     THPrint h;
  1989.     GrafPtr savePort;
  1990.     TPPrPort port;
  1991.     register TEPtr pTE;
  1992.     int ascent, nlines, i;
  1993.     Rect pageRect;
  1994.     Point where;
  1995.     char buf[BUFSIZ];
  1996.     TPrStatus status;
  1997.     
  1998.         /*  set up job record  */
  1999.     
  2000.     h = (THPrint) NewHandle(sizeof(TPrint));
  2001.     PrintDefault(h);
  2002.     if (hPrint) {
  2003.         PrJobMerge(hPrint, h);
  2004.         DisposHandle(hPrint);
  2005.     }
  2006.     else {
  2007.         InitCursor();
  2008.         if (!PrJobDialog(h)) {
  2009.             noPrint = 1;
  2010.             return;
  2011.         }
  2012.     }
  2013.     hPrint = h;
  2014.     
  2015.         /*  set up printing port  */
  2016.     
  2017.     GetPort(&savePort);
  2018.     port = PrOpenDoc(h, NULL, NULL);
  2019.     pTE = *c.hTE;
  2020.     TextFont(pTE->txFont);
  2021.     TextSize(pTE->txSize);
  2022.     TextFace(pTE->txFace);
  2023.     ascent = pTE->fontAscent;
  2024.     pageRect = (**h).prInfo.rPage;
  2025.     nlines = (pageRect.bottom - pageRect.top) / c.height;
  2026.     where.h = pageRect.left + 36;    /* leave 1/2 inch margin */
  2027.  
  2028.         /*  print each page  */
  2029.  
  2030.     rewind(c.echo2fp);
  2031.     c.echo2fp->binary = 0;
  2032.     do {
  2033.         PrOpenPage(port, NULL);
  2034.         where.v = pageRect.top + ascent;
  2035.         for (i = 0; i < nlines && fgets(buf, sizeof buf, c.echo2fp); i++) {
  2036.             MoveTo(where.h, where.v);
  2037.             DrawText(buf, 0, strlen(buf) - 1);
  2038.             where.v += c.height;
  2039.         }
  2040.         PrClosePage(port);
  2041.     } while (!PrError() && !feof(c.echo2fp));
  2042.     
  2043.         /*  done printing  */
  2044.     
  2045.     PrCloseDoc(port);
  2046.     SetPort(savePort);
  2047.     if ((**h).prJob.bJDocLoop == bSpoolLoop && !PrError())
  2048.         PrPicFile(h, NULL, NULL, NULL, &status);
  2049. }
  2050.